/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * ttp-hg-action.c
 * Copyright (C)  2011 Flos Lonicerae <lonicerae@gmail.com>
 * 
 * ttp-hg-action.c is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
   This software is based in part on the work of thunar-vcs-plugin written
   by Peter de Ridder <peter@xfce.org> and thunar-archive-plugin written by
   Benedikt Meurer <benny@xfce.org>
 */

#include <thunarx/thunarx.h>
#include <thunar-vfs/thunar-vfs.h>
#include <string.h>
#include <sys/wait.h>
#include "ttp-hg-action.h"


struct _TtpHgActionClass
{
	GtkActionClass parent_class;
};

struct _TtpHgAction
{
	GtkAction parent_instance;

	struct {
		unsigned is_parent : 1;
		unsigned is_directory : 1;
		unsigned is_file : 1;
	} property;

	GList *files;
	GtkWidget *window;
};

enum {
	PROPERTY_IS_PARENT = 1,
	PROPERTY_IS_DIRECTORY,
	PROPERTY_IS_FILE
};

enum {
	SIGNAL_NEW_PROCESS = 0,
	SIGNAL_COUNT
};


//G_DEFINE_TYPE (TtpHgAction, ttp_hg_action, G_TYPE_OBJECT);

static guint action_signal[SIGNAL_COUNT];
static GQuark ttp_action_arg_quark = 0;

static void
ttp_hg_action_init (TtpHgAction *self);

static void
ttp_hg_action_finalize (GObject *object);

static void
ttp_hg_action_class_init (TtpHgActionClass *klass);

static GtkWidget*
ttp_hg_action_create_menu_item(GtkAction *action);

static void
ttp_hg_action_set_property(GObject *object, guint property_id,
			   const GValue *value, GParamSpec *pspec);

static void
ttp_action_exec (GtkAction *item, TtpHgAction *ttp_action);

static gboolean
ttp_action_show_out_msg(GIOChannel *source, GIOCondition condiction, gpointer user_data);

static void
ttp_action_msgbox_close(GtkWidget *dialog, gpointer *user_data);

static void
ttp_action_add_subaction(GtkAction *action, GtkMenuShell *menu,
	      const gchar *name, const gchar *text,
	      const gchar *tooltip, const gchar *stock,
	      gchar *arg);


THUNARX_DEFINE_TYPE (TtpHgAction, ttp_hg_action, GTK_TYPE_ACTION)



static void
ttp_hg_action_init (TtpHgAction *self)
{
	/* TODO: Add initialization code here */
	self->files = NULL;
	self->window = NULL;
	self->property.is_parent = 0;
}

static void
ttp_hg_action_finalize (GObject *object)
{
	/* free elems of object itself */
	thunarx_file_info_list_free(TTP_HG_ACTION(object)->files);

	TTP_HG_ACTION(object)->files = NULL;
	TTP_HG_ACTION(object)->window = NULL;

	/* destroy parent class */
	G_OBJECT_CLASS (ttp_hg_action_parent_class)->finalize (object);
}

static void
ttp_hg_action_set_property(GObject *object, guint property_id,
			   const GValue *value, GParamSpec *pspec)
{
	switch(property_id)
	{
		case PROPERTY_IS_PARENT:
			TTP_HG_ACTION(object)->property.is_parent = g_value_get_boolean(value)?1:0;
			break;

		case PROPERTY_IS_DIRECTORY:
			TTP_HG_ACTION(object)->property.is_directory = g_value_get_boolean(value)?1:0;
			break;

		case PROPERTY_IS_FILE:
			TTP_HG_ACTION(object)->property.is_file = g_value_get_boolean(value)?1:0;
			break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
			break;
	}
}

static void
ttp_hg_action_class_init (TtpHgActionClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	GtkActionClass *parent_class = GTK_ACTION_CLASS(klass);

	object_class->finalize = ttp_hg_action_finalize;
	/* set property */
	object_class->set_property = ttp_hg_action_set_property;

	parent_class->create_menu_item = ttp_hg_action_create_menu_item;
	/* install properties */
	g_object_class_install_property(object_class, PROPERTY_IS_PARENT,
		g_param_spec_boolean("is-parent", "", "", FALSE, G_PARAM_CONSTRUCT_ONLY|G_PARAM_WRITABLE));
	g_object_class_install_property(object_class, PROPERTY_IS_DIRECTORY,
		g_param_spec_boolean("is-directory", "", "", FALSE, G_PARAM_CONSTRUCT_ONLY|G_PARAM_WRITABLE));
	g_object_class_install_property(object_class, PROPERTY_IS_FILE,
		g_param_spec_boolean("is-file", "", "", FALSE, G_PARAM_CONSTRUCT|G_PARAM_WRITABLE));

	/* arg quark for sending command */
	ttp_action_arg_quark = g_quark_from_static_string("ttp-action-arg");
}


static void
ttp_action_add_subaction(GtkAction *action, GtkMenuShell *menu,
	      const gchar *name, const gchar *text,
	      const gchar *tooltip, const gchar *stock,
	      gchar *arg)
{
	GtkAction *subaction;
	GtkWidget *subitem;

	subaction = gtk_action_new(name, text, tooltip, stock);
	/* set quark data for submenu action */
	g_object_set_qdata(G_OBJECT(subaction), ttp_action_arg_quark, arg);
	/* signal connect */
	g_signal_connect_after(subaction, "activate", G_CALLBACK(ttp_action_exec), action);
	/* encapsulate the action */
	subitem = gtk_action_create_menu_item(subaction);
	g_object_get(G_OBJECT(subaction), "tooltip", &tooltip, NULL);
	gtk_widget_set_tooltip_text(subitem, tooltip);
	gtk_menu_shell_append(menu, subitem);
	gtk_widget_show(subitem);
}

static GtkWidget*
ttp_hg_action_create_menu_item(GtkAction *action)
{
	GtkWidget *item;
	GtkWidget *menu;
	/* FIXME */
	TtpHgAction *ttp_action = TTP_HG_ACTION(action);
	item = GTK_ACTION_CLASS(ttp_hg_action_parent_class)->create_menu_item(action);

	menu = gtk_menu_new();
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);

	g_message("is_p:%d, is_d:%d, is_f:%d", ttp_action->property.is_parent, ttp_action->property.is_directory, ttp_action->property.is_file);

	/* parent: clone, init, userconfig, repoconfig, filter, archive, verify, about */
	/* right: add, commit, sync, log, update, vdiff, forget, revert, remove */
	/* not impl: blame, import, merge, mpatch, serve, shelve/unshelve, strip, thgstatus, rename */
	/* ignore: version, help, guess, status */
	if(ttp_action->property.is_parent) {
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::clone", Q_("Menu|Clone"), _("Clone"),
					 GTK_STOCK_COPY, "clone");
		/*
		there's a bug in hgtk, while init a repo
		in the directory which has been inited. :P
		*/
//		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
//					 "ttp::init", Q_("Menu|Init"), _("Init"),
//					 GTK_STOCK_PASTE, "init");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::userconfig", Q_("Menu|Usercfg"), _("User config"),
					 GTK_STOCK_ORIENTATION_PORTRAIT, "userconfig");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::repoconfig", Q_("Menu|Repocfg"), _("Repo config"),
					 GTK_STOCK_PROPERTIES, "repoconfig");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::filter", Q_("Menu|Filter"), _("Filter"),
					 GTK_STOCK_DIALOG_QUESTION, "filter");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::archive", Q_("Menu|Archive"), _("Archive"),
					 GTK_STOCK_FLOPPY, "archive");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::verify", Q_("Menu|Verify"), _("Verify"),
					 GTK_STOCK_FIND, "verify");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::about", Q_("Menu|About"), _("About"),
					 GTK_STOCK_ABOUT, "about");
	}
	else {
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::add", Q_("Menu|Add"), _("Add"),
					 GTK_STOCK_ADD, "add");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::commit", Q_("Menu|Commit"), _("Commit"),
					 GTK_STOCK_APPLY, "commit");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::sync", Q_("Menu|Sync"), _("Sync"),
					 GTK_STOCK_NETWORK, "sync");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::log", Q_("Menu|Log"), _("Log"),
					 GTK_STOCK_INDEX, "log");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::update", Q_("Menu|Update"), _("Update"),
					 GTK_STOCK_REFRESH, "update");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::vdiff", Q_("Menu|Vdiff"), _("Vdiff"),
					 GTK_STOCK_CONVERT, "vdiff");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::forget", Q_("Menu|Forget"), _("Forget"),
					 GTK_STOCK_REMOVE, "forget");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::revert", Q_("Menu|Revert"), _("Revert"),
					 GTK_STOCK_REDO, "revert");
		ttp_action_add_subaction(action, GTK_MENU_SHELL(menu),
					 "ttp::remove", Q_("Menu|Remove"), _("Remove"),
					 GTK_STOCK_DELETE, "remove");

	}
	return item;
}

static void
ttp_action_msgbox_close(GtkWidget *dialog, gpointer *user_data)
{
	gtk_widget_destroy(GTK_WIDGET(dialog));
}

static gboolean
ttp_action_show_out_msg(GIOChannel *source, GIOCondition condiction, gpointer user_data)
{
	/* GTK_WIDGET(user_data) would cause a runtime warning */
	GtkWidget *out_dialog = user_data;
	gchar *line;

	if(condiction & G_IO_IN) {
		while(g_io_channel_read_line(source, &line, NULL, NULL, NULL) == G_IO_STATUS_NORMAL)
		{
			gtk_message_dialog_format_secondary_text(
						GTK_MESSAGE_DIALOG(out_dialog),
						"%s", line);
			g_signal_connect(G_OBJECT(out_dialog), "response", G_CALLBACK(ttp_action_msgbox_close), NULL);
			gtk_dialog_run(GTK_DIALOG(out_dialog));
			g_free(line);
		}
	}

	if(condiction & G_IO_HUP) {
		g_io_channel_unref(source);
		g_message("channel unrefed");
		return FALSE;
	}
	return TRUE;
}

static void
ttp_action_child_exit(GPid pid, gint status, gpointer user_data)
{
	g_spawn_close_pid(pid);
	g_message("spawn close");
}

static void
ttp_action_exec (GtkAction *item, TtpHgAction *ttp_action)
{
	guint size, i;
	gchar **argv;
	GList *iter;
	gchar *uri;
	gchar *filename;
	gchar *file;
	gint pid;
	gint fd_out, fd_err;
	GIOChannel *chan_out, *chan_err;
	GError *error = NULL;

	/* get screen from window */
	GdkScreen *screen = gtk_window_get_screen (GTK_WINDOW (ttp_action->window));
	/* get files */
	iter = ttp_action->files;

	size = g_list_length (iter);
	/* only one item should be selected */
	g_message("size:%d", size);
	if (size != 1) {
		return;
	}

	argv = g_new (gchar *, 5);
	/* cmd */
	argv[0] = g_strdup ("hgtk");
	/* options */
	argv[1] = g_strdup (g_object_get_qdata (G_OBJECT (item), ttp_action_arg_quark));
	/* repo optons */
	argv[2] = g_strdup("-R");
	g_message("argv2:%s", argv[2]);
	/* last arg is 'NULL' */
	argv[4] = NULL;
	/*
	example:
	hgtk log -R PATH_TO_REPO 'NULL terminated'
	*/

	/* FIXME: check for what */
	if(iter) {
		/* left part */
		if(ttp_action->property.is_parent) {
			/* get uri */
			uri = thunarx_file_info_get_uri (iter->data);
		}
		/* right part */
		else {
			/* get parent uri */
			uri = thunarx_file_info_get_parent_uri (iter->data);
		}
	}

	if (G_LIKELY(uri != NULL)) {
		g_message("uri:%s", uri);
		filename = g_filename_from_uri(uri, NULL, NULL);
		g_message("filename:%s", filename);
		if(G_LIKELY(filename != NULL)) {
			/* strip the "file://" part of the uri */
			if(strncmp(filename, "file://", 7) == 0) {
				file = g_strdup(filename + 7);
			}
			else {
				file = g_strdup(filename);
			}
			g_message("file:%s", file);

			argv[3] = file;
			g_message("file:%s", file);
			/* release the filename */
			g_free(filename);
		}
		g_free(uri);
	}

	pid = 0;

	if(!gdk_spawn_on_screen_with_pipes(
		screen, NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
		NULL, NULL, &pid,
		NULL, &fd_out, &fd_err, &error))
	{
		/* XXX
		gtk_message_dialog_new (parent?GTK_WINDOW(parent):NULL,
					parent?GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL:0,
					GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed"));
		*/
		GtkWidget *dialog = gtk_message_dialog_new(
					GTK_WINDOW (ttp_action->window),
					GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL,
					GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
					_("Could not spawn 'hgtk'"));
		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		g_error_free (error);
	}
	else {
		GtkWidget *out_dialog = gtk_message_dialog_new(
					GTK_WINDOW(ttp_action->window),
					GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL,
					GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
					_("Warning"));
		/* must add a child_watch */
		g_child_watch_add(pid, (GChildWatchFunc)ttp_action_child_exit, NULL);
		chan_out = g_io_channel_unix_new(fd_out);
		g_io_add_watch(chan_out, G_IO_IN|G_IO_HUP, (GIOFunc)ttp_action_show_out_msg, out_dialog);
	}
	g_strfreev (argv);
}

GtkAction*
ttp_hg_action_new(const gchar *name,
		  const gchar *label,
		  GList *files,
		  GtkWidget *window,
		  gboolean is_parent,
		  gboolean is_directory,
		  gboolean is_file)
{
	GtkAction *action;
	g_return_val_if_fail(name, NULL);
	g_return_val_if_fail(label, NULL);

	action = g_object_new(TTP_TYPE_HG_ACTION,
			      "hide-if-empty", FALSE,
			      "name", name,
			      "label", label,
			      "is-parent", is_parent,
			      "is-directory", is_directory,
			      "is-file", is_file,
		      #if !GTK_CHECK_VERSION(2,9,0)
			      "stock-id", "mercurial",
		      #else
			      "icon-name", "mercurial",
		      #endif
			      NULL);

	if (action == NULL) {
		g_message("create action failed...");
		return NULL;
	}

	TTP_HG_ACTION(action)->files = thunarx_file_info_list_copy(files);
	TTP_HG_ACTION(action)->window = window;
	return (GtkAction *)action;
}


